<!--
properties:
  @prop  treeDataUrl     树菜单的数据读取地址，无需转化为树结构，String
  @prop  treeParam       树加载参数, Object
  @prop  tableDataUrl    表格数据读取地址，无需转化为树结构，String
  @Prop  tableParam      表格加载参数， Object
  @prop  addUrl          数据新增地址，String
  @prop  editUrl         数据编辑地址，String
  @prop  deleteUrl       数据删除地址，String
  @prop  treeTitle       树菜单标题，String
  @prop  tableTitle      表格标题，String
  @prop  treeProps       用于树菜单子属性及标签属性名称，默认{children, label}
  @prop  tableProps      用于定于表格数据父属性名称，默认{parentId}
  @prop  columns         表格列定义{prop, label, width, filters, filterMethod, filterPlacement}，其中filters必须通过方法传进去
  @prop  emptyTableText  表格数据为空时的提示语句，默认为‘暂无数据’, String
  @prop  searchHint      搜索框提示，String
  @prop  searchProps     用于搜索的属性名，Array
  @prop  form            表单对象，Object
  @prop  formDialogSize  表单对话框的尺寸，默认tiny，可选tiny/small/large/full, String
  @prop  loadingForm     是否显示表单对话框加载进度条，Boolean
  @prop  formLoadingText 表单加载进度条显示的提示语句，String
  @prop  rules           表单校验规则，Object
events:
  @event  onNodeClick    树节点点击事件，传入对应树节点的data，Object
  @event  beforeAdd      新增前事件，undefined
  @event  beforeEdit     编辑前事件，传入编辑的行data，Object
  @event  onUpdate        正在新增或者修改，传入新增或修改的对象，Object
  @event  onAssociate    关系绑定事件，undefined
  @event  onTableCreated 表格创建前的事件，传入表格数据，Array
  @event  onTreeCreated  树菜单创建事件，传入树菜单数据，Array
slots:
  @slot  form           对话框表单内容
  @slot  dialog         对话框置于此
  @slot  buttonArea     按钮区域
  @slot  customButton   是否自定义列
  @slot  beforeColumns  自定义前列
  @slot  afterColumns   自定义后列
-->
<template>
  <!-- 主内容区 -->
  <div class="row">

    <!--  左侧树形菜单  -->
    <div class="col-md-3">

      <!--树形菜单上方按钮-->
      <template>
        <a v-if="canAdd" class="btn btn-primary btn-block margin-bottom" @click.prevent="handleAdd">新增</a>
        <a v-if="canAssociate" class="btn btn-primary btn-block margin-bottom" @click.prevent="handleAssociate">关系绑定</a>
      </template>
      <!--  树形菜单  -->
      <div class="box box-primary">
        <div class="box-header with-border">
          <h3 class="box-title">{{treeTitle}}</h3>
        </div>
        <el-scrollbar tag="div" class="box-body no-padding" style="height: 100%">
          <el-tree
            class="tree-view-scrollable"
            node-key="id"
            :data="transformTreeData"
            :props="treeProps"
            :default-expanded-keys="[selectedNode.id]"
            :current-node-key="selectedNode.id"
            :render-content="renderContent"
            @node-click="handleNodeClick"
            auto-expand-parent
            highlight-current
            accordion>
          </el-tree>
        </el-scrollbar>
      </div>
    </div>

    <!--  右侧表格区域  -->
    <div class="col-md-9">
      <!--  数据量比较少，分页放在前端  -->
      <Table
        :title="tableTitle"
        :searchHint="searchHint"
        :pageData="tablePageData"
        :emptyText="emptyTableText"
        @search="search"
        @sizeChange="pageSizeChange"
        @pageTurning="pageTurning"
        @filterChange="filterChange"
        @sortChange="sortChange">

        <!--表格上方的按钮区-->
        <template slot="buttonArea">
          <slot name="buttonArea"></slot>
        </template>

        <!--自定义前列-->
        <slot name="beforeColumns"></slot>

        <el-table-column v-for="col of columns"
                         align="center"
                         :key="col.prop"
                         :column-key="col.columnKey"
                         :prop="col.prop"
                         :label="col.label"
                         :width="col.width"
                         :min-width="col.minWidth"
                         :sortable="col.sortable"
                         :filters="col.filters"
                         :filter-placement="col.filterPlacement">
        </el-table-column>

        <!--自定义后列-->
        <slot name="afterColumns"></slot>

        <!--表格操作列-->
        <el-table-column v-if="canOperate" label="操作" align="center" :width="btnColWidth">
          <template slot-scope="scope">
            <el-button v-if="canEdit" size="mini" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button v-if="canManage" size="mini" type="success" @click="handleManage(scope.row)">管理</el-button>
            <el-button v-if="canGrant" size="mini" type="info" @click="handleGrant(scope.row)">授权</el-button>
            <el-button v-if="canDelete" size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </Table>
    </div>
    <!--  新增或编辑对话框  -->
    <el-dialog v-if="canOperate" :title="updateDialogTitle" :size="formDialogSize" v-model="isUpdateDialogVisible" :close-on-click-modal="closeOnClickModal">
      <!--  新增或编辑对话框  -->
      <el-form :model="form" :rules="rules" ref="form" label-width="100px" v-loading="loadingForm" :element-loading-text="formLoadingText">
        <slot name="form"></slot>
      </el-form>
      <slot name="formExtra"></slot>
      <div slot="footer" class="dialog-footer">
        <el-button @click="isUpdateDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="update" :loading="isUpdating">确 定</el-button>
      </div>
    </el-dialog>
    <!-- 额外的对话框 -->
    <slot name="dialog"></slot>
  </div>
</template>

<script>
  import Table from "../../components/content/bridge/Table.vue"
  import {doGet,doPost,doDelete} from "../../assets/js/utils/RequestUtils"
  import {treeOf,childrenArrayOf} from "../../assets/js/utils/TreeUtils"
  import {LOADING,COMPLETE} from "../../store/mutationTypes"
  import {getAuthorizedFunction, hasAuthorizedFunction} from "../../assets/js/global"
  import {ADD, EDIT, DELETE, ASSOCIATE, MANAGE, GRANT} from "../../assets/js/enum/FunctionType"
  import {ASCENDING, DESCENDING} from "../../assets/js/enum/SortType"

  export default {
    name: "FrontSideTreeTable",
    props:{
      /*  树形菜单数据加载地址  */
      treeDataUrl: {
        type: String,
        required:true
      },

      /*  树加载参数 */
      treeParam: Object,

      /* 表格数据加载地址 */
      tableDataUrl: {
        type: String,
        required:true
      },

      /*  表格加载参数 */
      tableParam: Object,

      /*  数据新增地址  */
      addUrl: String,

      /*  数据编辑地址  */
      editUrl: String,

      /*  数据删除地址  */
      deleteUrl: String,

      /*  树形菜单标题  */
      treeTitle: {
        type:String,
        required:true
      },

      /*  树节点属性  */
      treeProps: {
        type: Object,
        default() {
          return {
            children: 'children',
            label: 'name'
          }
        }
      },

      /** 树菜单的render函数 */
      renderContent:Function,

      /*  表格标题  */
      tableTitle: {
        type:String,
        required:true
      },

      /*  表格属性  */
      tableProps: {
        type: Object,
        default() {
          return {
            parentId: "parentId"
          }
        }
      },

      /*  表格所包含的列[{prop,label,width,sortable,filter,filterMethod}]  */
      columns: {
        type: Array,
        required:true
      },

      /* 表格无数据时的提示语句 */
      emptyTableText: {
        type:String,
        default:"暂无数据"
      },

      /*  搜索框提示语句  */
      searchHint: {
        type: String,
        default:"查找..."
      },

      /*  用于搜索的属性名  */
      searchProps: {
        type: Array,
        default() {
          return [];
        }
      },

      //表单对象
      form:{
        type: Object,
        default() {
          return {
            //表单对象
            parentId: ""
          }
        }
      },

      //表单对话框的尺寸
      formDialogSize:{
        type:String,
        default:"tiny"
      },

      //表单加载进度圈
      loadingForm: {
        type: Boolean,
        default: false
      },

      //表单加载进度圈下的文字
      formLoadingText: {
        type:String,
        default: "正在加载"
      },

      /*  表单校验规则 */
      rules: Object,

      /* 是否可管理 */
      manageable: {
        type:Boolean,
        default:false
      },

      /* 所有的按钮是否全部自定义 */
      customButton: {
        type: Boolean,
        default:false
      }
    },
    data() {
      return {
        //分页数据
        pageData:{
          data: [],
          pageSize: 10,
          currentPage: 1
        },

        //树形菜单数据
        treeData: [],

        //被选中的节点
        selectedNode:{id:""},

        //搜索的字符串
        searchStr: "",

        //点击外侧不关闭对话框
        closeOnClickModal:false,

        //是否正在更新，是则显示按钮加载圈
        isUpdating:false,

        //是否显示新增/编辑对话框
        isUpdateDialogVisible:false,

        //  新增还是编辑
        updateType:"",

        //排序属性
        sortProp:{
          prop:"",
          order:ASCENDING
        },

        //筛选条件
        filters:{prop:"", values:[]}
      }
    },
    computed: {
      /* 前端分页 */
      tablePageData() {
        let result = {
          pageSize: this.pageData.pageSize,
          currentPage: this.pageData.currentPage
        };
        let filterData = [];
        if(Array.isArray(this.pageData.data)) {
          //获取选中树节点的数组
          if(this.selectedNode.id) {
            if(this.treeDataUrl === this.tableDataUrl) {
              filterData = childrenArrayOf(this.selectedNode, this.pageData.data, true, this.tableProps.parentId);
            } else {
              let childrenData = childrenArrayOf(this.selectedNode, this.treeData, true);
              childrenData.forEach(item=>filterData.push(...childrenArrayOf(item, this.pageData.data, false, this.tableProps.parentId)));
            }
          } else {
            filterData = this.pageData.data;
          }
          //筛选搜索框
          filterData = filterData.filter(item =>this.searchStr.split(" ").every(str=>this.searchProps.some(prop=>!!item[prop] && item[prop].includes(str))));
        }
        //筛选条件
        if(!!this.filters.prop && this.filters.values.length > 0) {
          filterData = filterData.filter(item=>this.filters.values.includes(item[this.filters.prop]));
        }
        //排序
        if(!!this.sortProp.prop) {
          if (this.sortProp.order === DESCENDING) {
            Array.sort(filterData, (data1, data2) => data2[this.sortProp.prop] - data1[this.sortProp.prop]);
          } else {
            Array.sort(filterData, (data1, data2) => data1[this.sortProp.prop] - data2[this.sortProp.prop]);
          }
        }
        //分页
        result.total = filterData.length;
        result.data = filterData.slice(
          (result.currentPage - 1) * result.pageSize,
          (result.currentPage) * result.pageSize
        );
        //返回结果
        return result;
      },

      //转化后的树结构数据
      transformTreeData() {
        return treeOf(this.treeData);
      },

      /*  对话框标题  */
      updateDialogTitle() {
        switch(this.updateType) {
          case ADD:
            return "新增";
          case EDIT:
            return "编辑";
          default:
            return "新增";
        }
      },
      canOperate() {
        return this.canEdit || this.canManage || this.canDelete || this.canGrant;
      },
      canAdd() {
        return hasAuthorizedFunction(this, ADD) && !this.customButton;
      },
      canEdit() {
        return hasAuthorizedFunction(this, EDIT) && !this.customButton;
      },
      canDelete() {
        return hasAuthorizedFunction(this, DELETE) && !this.customButton;
      },
      canAssociate() {
        return hasAuthorizedFunction(this, ASSOCIATE) && !this.customButton;
      },
      canManage() {
        return hasAuthorizedFunction(this, MANAGE) && this.manageable && !this.customButton;
      },
      canGrant() {
        return hasAuthorizedFunction(this, GRANT) && !this.customButton;
      },
      btnColWidth() {
        let width = 0;
        if(this.canAdd) width += 45;
        if(this.canEdit) width += 45;
        if(this.canDelete) width += 45;
        if(this.canAssociate) width += 45;
        if(this.canManage) width += 45;
        if(this.canGrant) width += 45;
        return width;
      }
    },

    methods:{

      /* 树形菜单节点点击事件 */
      handleNodeClick(data, node) {
        this.selectedNode = {...data};
        this.pageData.currentPage = 1;
        this.$emit("onNodeClick", data);
      },

      /* 查找 */
      search(searchStr) {
        this.searchStr = searchStr;
      },

      /* 页面尺寸变化事件 */
      pageSizeChange(newSize) {
        this.pageData.pageSize = newSize;
      },

      /* 当前页调整事件 */
      pageTurning(curPage) {
        this.pageData.currentPage = curPage;
      },

      /* 筛选事件 */
      filterChange(filters) {
        this.filters = filters;
      },

      /* 排序事件 */
      sortChange({column, prop, order}) {
        this.sortProp = {column, prop, order}
      },

      /*  新增行  */
      handleAdd() {
        this.$emit("beforeAdd");
        this.updateType = ADD;
        this.isUpdateDialogVisible = true;
        if(this.$refs["form"]) this.$refs["form"].resetFields();
      },

      /*  关系绑定 */
      handleAssociate() {
        this.$emit("onAssociate");
      },

      /* 管理事件 */
      handleManage(data) {
        this.$emit("onManage", data);
      },

      /* 授权事件 */
      handleGrant(data) {
        this.$emit("onGrant", data);
      },

      /*  编辑行  */
      handleEdit(rowData) {
        this.$emit("beforeEdit", rowData);
        this.updateType = EDIT;
        this.isUpdateDialogVisible = true;
        if(this.$refs["form"]) this.$refs["form"].resetFields();
      },

      /*  删除行  */
      handleDelete(rowData) {
        this.$confirm("此操作将永久删除该条数据以及对应子数据, 是否继续?", "提示", {
          confirmButtonText:"确定",
          cancelButtonText:"取消",
          type:"warning"
        }).then(()=>{
          doDelete(this.deleteUrl, {
            data:rowData,
            menuFunctionId:getAuthorizedFunction(this, DELETE),
            component:this
          })
            .then(response => {
              this.$message({message:response.message, type:response.status.toLowerCase()});
              this.fetchData();
            });
        });
      },

      /* 新增或者编辑 */
      update() {
        let updateUrl = this.addUrl;
        if(this.updateType === EDIT) updateUrl = this.editUrl;
        this.$refs["form"].validate(valid=>{
          if(valid) {
            this.isUpdating = true;
            this.$emit("onUpdate", this.form);
            doPost(updateUrl, {
              data: this.form,
              menuFunctionId:getAuthorizedFunction(this, this.updateType),
              component: this
            })
              .then(response => this.$message({message: response.message, type: response.status.toLowerCase()}))
              .finally(()=>{
                this.isUpdating = false;
                this.isUpdateDialogVisible = false;
                this.fetchData();
              });
          }
        });
      },

      /* 获取表格数据 */
      fetchData() {
        //打开页面加载状态
        this.$store.commit(LOADING);
        doGet(this.tableDataUrl, {data:this.tableParam, component:this})
          .then(response=>{
            this.pageData.data = response;
            //避免重复获取数据
            if(this.tableDataUrl === this.treeDataUrl) {
              this.treeData = this.pageData.data;
              this.$store.commit(COMPLETE)
            } else {
              this.fetchTreeData();
            }
            this.$emit("onTableCreated", this.pageData.data);
          })
          .catch(error=>this.$store.commit(COMPLETE));
      },

      /* 获取树形菜单数据 */
      fetchTreeData() {
        //打开页面加载状态
        this.$store.commit(LOADING);
        doGet(this.treeDataUrl, {data:this.treeParam, component:this})
          .then(response=>{
            this.treeData = response;
            //返回没有转化为树结构的原始数据
            this.$emit("onTreeCreated", response);
          })
          .finally(()=>this.$store.commit(COMPLETE));
      }
    },

    /* 用到的组件 */
    components:{Table},

    /* 根据路由变化实时获取最新数据 */
    activated() {
      this.fetchData();
    },

    /* 生命周期，被创建 */
    created() {
      this.fetchData();
    }
  }
</script>

<style scoped>
  .tree-view-scrollable {
    max-height: 600px;
    border: 0;
  }
</style>



// WEBPACK FOOTER //
// FrontSideTreeTable.vue?293aef59
